<?php
#
# dmBridge: a data access framework for CONTENTdm(R)
#
# Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * @author Alex Dolski <alex.dolski@unlv.edu>
 * @license http://www.opensource.org/licenses/mit-license.php
 */
class DMAPIObjectController extends DMAbstractController {

	/**
	 * Outputs an object's bitstream (file), without any processing.
	 *
	 * @param string aliasptr Comma-separated alias/pointer pair
	 */
	public function bitstream($aliasptr) {
		$tmp = explode(",", $aliasptr);
		try {
			$col = DMCollectionFactory::getCollection(
					DMCollection::getSanitizedAlias($tmp[0]));
			$obj = DMObjectFactory::getObject($col, (int) $tmp[1]);
			
			$file = $obj->getFile();
			$media_type = $file->getMediaType();
			if (!$media_type) {
				$media_type = new DMMediaType("application", "octet-stream");
			}

			$response = new DMHTTPResponse();
			$rep = new DMHTTPRepresentation();
			$rep->setMediaType($media_type);
			$rep->setBody($file);
			$response->setRepresentation($rep);
			$response->send();
			die;
		} catch (DMException $e) {
			bailOut($e);
		}
	}
	
	/**
	 * Outputs all comments for a particular object.
	 *
	 * @param string aliasptr Comma-separated alias/pointer pair
	 */
	public function comments($aliasptr) {
		if (DMHTTPRequest::getCurrent()->getMethod() == DMHTTPMethod::POST) {
			$this->postComment($aliasptr);
		} else {
			$this->viewComments($aliasptr);
		}
	}

	/**
	 * Outputs an excerpt of an object's bitstreams. Currently the only
	 * supported format is PDF.
	 *
	 * @param string aliasptr Comma-separated alias/pointer pair
	 */
	public function excerpt($aliasptr) {
		$tmp = explode(",", $aliasptr);
		try {
			$col = DMCollectionFactory::getCollection(
					DMCollection::getSanitizedAlias($tmp[0]));
			$obj = DMObjectFactory::getObject($col, (int) $tmp[1]);

			$response = new DMHTTPResponse();
			$rep = new DMHTTPRepresentation();

			$file = $obj->getFile();
			$media_type = $file->getMediaType();

			if ($media_type->equals(new DMMediaType("application", "pdf"))) {
				$rep->setMediaType($media_type);
				$uri = $this->getHTTPRequest()->getURI();
				$page = $uri->getQueryValue("page");
				if (!$page || round($page) != $page) {
					throw new DMIllegalArgumentException("Invalid page number.");
				}
				
				$cmd = new DMShellCommand(sprintf("%s -q -sDEVICE=pdfwrite "
					. "-dNOPAUSE -dBATCH -dSAFER -dFirstPage=%d -dLastPage=%d "
					. "-sOutputFile=%%stdout %s",
					DMConfigIni::getInstance()->getString("dmbridge.ghostscript_path"),
					$page, $page, $obj->getFile()->getPathname()));
				$rep->setBody($cmd);
			} else {
				$e = new DMException("This object does not support excerpts.");
				$e->setHTTPResponseCode(404);
				throw $e;
			}

			$response->setRepresentation($rep);
			$response->send();
			die;
		} catch (DMException $e) {
			bailOut($e);
		}
	}

	private function postComment($alias_ptr) {
		$tmp = explode(",", $alias_ptr);
		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$response->setRepresentation($rep);

		try {
			$alias = DMCollection::getSanitizedAlias($tmp[0]);
			$ptr = (int) $tmp[1];
			$col = DMCollectionFactory::getCollection($alias);
			$obj = DMObjectFactory::getObject($col, $ptr);

			$moderation = true;
			$msg = "";
			if (DMConfigXML::getInstance()->isCommentModerationEnabled()) {
				$msg = "Comment added, subject to moderation.";
			} else {
				$msg = "Comment added.";
				$moderation = false;
			}

			$obj = DMObjectFactory::getObject(
					DMCollectionFactory::getCollection($alias), $ptr);

			$req_rep = DMHTTPRequest::getCurrent()->getRepresentation();
			$c = new DMComment();
			$c->setName(substr($req_rep->getFormValue("name"), 0, 100));
			$c->setEmail($req_rep->getFormValue("email"));

			$c->setValue(substr($req_rep->getFormValue("text"), 0, 10000));
			$c->setTimestamp(new DMDateTime());
			$c->setApproved(!$moderation);

			$obj->addComment($c);
			if (DMConfigXML::getInstance()->isCommentNotificationEnabled()) {
				$mailer = new DMMailer();
				$mailer->sendNotificationOfNewComment($c);
			}
			if ($moderation) {
				$response->setStatusCode(202);
			} else {
				$response->setStatusCode(201);
				$response->addHeader("Location",
						$c->getURI(DMBridgeComponent::HTTPAPI));
			}

			$response->send();
			die;
		} catch (DMUnavailableModelException $e) {
			bailOut($e);
		} catch (Exception $e) {
			bailOut($e);
		}
	}

	private function viewComments($aliasptr) {
		$tmp = explode(",", $aliasptr);
		$uri = DMHTTPRequest::getCurrent()->getURI();

		$page = abs((int) substr($uri->getQueryValue("page"), 0, 7));
		$page = ($page < 1) ? 1 : $page;
		$rpp = abs((int) substr($uri->getQueryValue("rpp"), 0, 4));
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;

		try {
			$col = DMCollectionFactory::getCollection(
					DMCollection::getSanitizedAlias($tmp[0]));
			$obj = DMObjectFactory::getObject($col, (int) $tmp[1]);

			$response = new DMHTTPResponse();
			$rep = DMHTTPResponseFactory::getRepresentation();
			$trans = DMHTTPResponseFactory::getTransformer();
			$rep->setBody($trans->transformObjectComments($obj, $page, $rpp));
			$response->setRepresentation($rep);
			$response->send();
			die;
		} catch (DMException $e) {
			bailOut($e);
		}
	}

	/**
	 * Outputs the highest-rated objects in a given collection
	 */
	public function highestRated($alias) {
		$col = DMCollectionFactory::getCollection(
				DMCollection::getSanitizedAlias($alias));
		$uri = DMHTTPRequest::getCurrent()->getURI();

		$rpp = abs((int) substr($uri->getQueryValue("rpp"), 0, 4));
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;


		$q = new DMObjectQuery();
		$q->addCollection($col);
		$q->setNumResultsPerPage($rpp);
		$q->setRatingRange(0, 1);
		// "prime" the query
		$q->getSearchResults();

		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$rep->setBody($trans->transformRatings($q));
		$response->setRepresentation($rep);
		$response->send();
		die;
	}

	/**
	 * Controls browse view.
	 *
	 * @param string alias Collection alias, as a string. If invalid or missing,
	 * the first allowed collection will be used. If that is invalid
	 * or missing, will browse all objects in the system.
	 * @since 0.1
	 */
	public function index($alias) {
		$alias = DMCollection::getSanitizedAlias($alias);
		if (strlen($alias) < 2) {
			$alias = "/dmdefault";
		}
		$alias = DMCollection::getSanitizedAlias($alias);
		$collection = DMCollectionFactory::getCollection($alias);

		// check for user input from a search
		$input = new DMInput(DMHTTPRequest::getCurrent()->getURI());
		$query = null;
		if ($input->isPresent()) {
			$query = $this->search($input, $collection);
		} else {
			$query = $this->browse($collection);
		}
		// execute search
		$query->getSearchResults();

		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$rep->setBody($trans->transformResults($query));
		$response->setRepresentation($rep);
		$response->send();
		die;
	}

	/**
	 * Invoked by index()
	 *
	 * @return DMObjectQuery
	 */
	private function browse(DMCollection $c) {
		$uri = DMHTTPRequest::getCurrent()->getURI();
		$sort = $uri->getQueryValue("sort")
				? substr($uri->getQueryValue("sort"), 0, 20) : null;
		$order = $uri->getQueryValue("order")
				? substr($uri->getQueryValue("order"), 0, 10) : null;
		$page = $uri->getQueryValue("page")
				? abs((int) $uri->getQueryValue("page")) : 1;
		$rpp = $uri->getQueryValue("rpp")
				? abs((int) $uri->getQueryValue("rpp")) : 20;
		if ($uri->getQueryValue("view")
				&& $uri->getQueryValue("view") == "feed") {
			$sort = array("dmmodified" => "desc");
			$page = 1;
		}

		// cap max results per page at 100
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;

		$q = new DMObjectQuery();
		$q->setCollections(array($c));
		$q->setSortFields(array($sort => $order));
		$q->setPage($page);
		$q->setNumResultsPerPage($rpp);
		return $q;
	}

	public function rating($alias_ptr) {
		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$response->setRepresentation($rep);

		$tmp = explode(",", $alias_ptr);
		try {
			$alias = DMCollection::getSanitizedAlias($tmp[0]);
			$ptr = (int) $tmp[1];
			$col = DMCollectionFactory::getCollection($alias);
			$obj = DMObjectFactory::getObject($col, $ptr);

			if (DMHTTPRequest::getCurrent()->getMethod() == DMHTTPMethod::POST) {
				$req_rep = DMHTTPRequest::getCurrent()->getRepresentation();
				$rating = new DMRating($req_rep->getFormValue("value"), 100);
				$obj->addRating($rating);
				$response->setStatusCode(204);
				$response->send();
				die;
			}

			$rep->setBody($trans->transformObjectRating($obj));
		} catch (UnavailableModelDMException $e) {
			$response->setStatusCode(404);
			$response->send();
			die;
		} catch (DMIllegalArgumentException $e) {
			$response->setStatusCode(400);
			$response->send();
			die;
		}

		$response->send();
		die;
	}

	/**
	 * Invoked by index()
	 *
	 * @return DMObjectQuery
	 */
	private function search(DMInput $input, DMCollection $col) {
		$uri = DMHTTPRequest::getCurrent()->getURI();
		$input->validate();

		$sort = $uri->getQueryValue("sort")
				? substr($uri->getQueryValue("sort"), 0, 10) : null;
		$order = $uri->getQueryValue("order")
				? substr($uri->getQueryValue("order"), 0, 4) : null;
		$page = $uri->getQueryValue("page")
				? abs((int) $uri->getQueryValue("page")) : 1;
		$rpp = $uri->getQueryValue("rpp")
				? abs((int) $uri->getQueryValue("rpp")) : 20;
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;

		$min_rating = $uri->getQueryValue("min_rating")
				? abs($uri->getQueryValue("min_rating")) : null;
		$max_rating = $uri->getQueryValue("max_rating")
				? abs($uri->getQueryValue("max_rating")) : null;

		$query = new DMObjectQuery();
		$query->setPredicates($input->getTerms());
		$query->setCollections($input->getCollections());
		$query->setSortFields(array($sort => $order));
		$query->setNumResultsPerPage($rpp);
		$query->setPage($page);
		if ($min_rating || $max_rating) {
			if ($max_rating > 100) $max_rating = 100;
			if ($min_rating < 0) $min_rating = 0;
			$qf = new DMRatingFilter();
			$qf->setRange($min_rating, $max_rating);
			$query->addFilter($qf);
		}

		foreach ($col->getFacets() as $f) {
			$query->addFacetTerm($f);
		}
		return $query;
	}

	/**
	 * @param string aliasptr Comma-separated alias and pointer
	 */
	public function compoundSearch($aliasptr) {
		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$response->setRepresentation($rep);
		$uri = DMHTTPRequest::getCurrent()->getURI();

		$tmp = explode(",", $aliasptr);
		$col = DMCollectionFactory::getCollection(
				DMCollection::getSanitizedAlias($tmp[0]));
		$ptr = abs((int) substr($tmp[1], 0, 8));
		$term = substr($uri->getQueryValue("term"), 0, 150);

		if (!strlen($term)) {
			$response->setStatusCode(400);
			$response->send();
			die;
		}

		$page = $uri->getQueryValue("page")
				? abs((int) $uri->getQueryValue("page")) : 1;
		$rpp = $uri->getQueryValue("rpp")
				? abs((int) $uri->getQueryValue("rpp")) : 20;
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;

		// instantiate an object so we can find its parent to search within
		$obj = DMObjectFactory::getObject($col, $ptr);
		$obj = ($obj->isChild()) ? $obj->getParent() : $obj;

		$qt = new DMQueryPredicate();
		$qt->setString($term);
		$qt->setField(new DMDCElement("full"));
		$qt->setMode("exact");

		$query = new DMObjectQuery();
		$query->setPredicates(array($qt));
		$query->setCollections(array($col));
		$query->setSortFields(array("title" => "asc"));
		$query->setNumResultsPerPage($rpp);
		$query->setPage($page);
		$query->addObject($obj);

		$query->getSearchResults();

		$rep->setBody($trans->transformResults($query));
		$response->send();
		die;
	}

	public function searchByRating() {
		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$response->setRepresentation($rep);
		$uri = DMHTTPRequest::getCurrent()->getURI();

		$page = (int) substr($uri->getQueryValue("page"), 0, 7);
		$rpp = (int) $uri->getQueryValue("rpp");
		$rpp = ($rpp > 100) ? 100 : $rpp;
		$rpp = ($rpp == 1) ? 50 : ($rpp < 10) ? 10 : $rpp;
		$min_rating = $uri->getQueryValue("min_rating");
		$max_rating = $uri->getQueryValue("max_rating");
		$collections = explode(",", $uri->getQueryValue("collections"));

		$q = new DMObjectQuery();
		$q->setPage($page);
		$q->setNumResultsPerPage($rpp);
		$q->setRatingRange((float) $min_rating, (float) $max_rating);
		foreach ($collections as $alias) {
			if ($alias) {
				$col = DMCollectionFactory::getCollection(
						DMCollection::getSanitizedAlias($alias));
				$q->addCollection($col);
			}
		}

		if ($min_rating == null || $max_rating == null
				|| $min_rating >= $max_rating) {
			$response->setStatusCode(400);
			$rep->setBody("Invalid rating.");
			$response->send();
			die;
		}

		// "prime" the query
		$q->getSearchResults();

		$rep->setBody($trans->transformRatings($q));
		$response->send();
		die;
	}

	public function tags($aliasptr) {
		if (DMHTTPRequest::getCurrent()->getMethod() == DMHTTPMethod::POST) {
			$this->postTag($aliasptr);
		} else {
			$this->viewTags($aliasptr);
		}
	}

	private function postTag($alias_ptr) {
		$tmp = explode(",", $alias_ptr);

		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$response->setRepresentation($rep);
		$req = DMHTTPRequest::getCurrent();

		try {
			$alias = DMCollection::getSanitizedAlias($tmp[0]);
			$ptr = (int) $tmp[1];
			$col = DMCollectionFactory::getCollection($alias);
			$obj = DMObjectFactory::getObject($col, $ptr);

			$req_rep = $req->getRepresentation();
			$t = new DMTag();
			$t->setValue(substr($req_rep->getFormValue("value"), 0, 100));
			$t->setTimestamp(new DMDateTime());

			$moderation = true;
			$msg = "";
			if (DMConfigXML::getInstance()->isTagModerationEnabled()) {
				$msg = "Tag added, subject to moderation.";
			} else {
				$msg = "Tag added.";
				$moderation = false;
			}
			$t->setApproved(!$moderation);
			$obj->addTag($t);

			if ($moderation) {
				$response->setStatusCode(202);
			} else {
				$response->setStatusCode(201);
				$response->addHeader("Location",
						$t->getURI(DMBridgeComponent::HTTPAPI));
			}
		} catch (DMException $e) {
			$response->setStatusCode($e->getHTTPResponseCode());
			$rep->setBody($trans->transformException($e));
		}

		$response->send();
		die;
	}

	/**
	 * Outputs an object's thumbnail image.
	 *
	 * @param string aliasptr Comma-separated alias/pointer pair
	 */
	public function thumbnail($aliasptr) {
		$tmp = explode(",", $aliasptr);
		try {
			$col = DMCollectionFactory::getCollection(
					DMCollection::getSanitizedAlias($tmp[0]));
			$obj = DMObjectFactory::getObject($col, (int) $tmp[1]);

			$file = $obj->getThumbnail();
			$media_type = $file->getMediaType();
			if (!$media_type) {
				$media_type = new DMMediaType("image", "jpeg");
			}

			$response = new DMHTTPResponse();
			$rep = new DMHTTPRepresentation();
			$rep->setMediaType($media_type);
			$rep->setBody($file);
			$response->setRepresentation($rep);
			$response->send();
			die;
		} catch (DMException $e) {
			bailOut($e);
		}
	}

	private function viewTags($aliasptr) {
		$tmp = explode(",", $aliasptr);

		$col = DMCollectionFactory::getCollection(
				DMCollection::getSanitizedAlias($tmp[0]));
		$obj = DMObjectFactory::getObject($col, (int) $tmp[1]);

		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$rep->setBody($trans->transformObjectTags($obj));
		$response->setRepresentation($rep);
		$response->send();
		die;
	}

	/**
	 * @param string aliasptr Comma-separated alias and pointer
	 */
	public function view($aliasptr) {
		$response = new DMHTTPResponse();
		$rep = DMHTTPResponseFactory::getRepresentation();
		$trans = DMHTTPResponseFactory::getTransformer();
		$response->setRepresentation($rep);

		$tmp = explode(',', $aliasptr);
		$alias = DMCollection::getSanitizedAlias($tmp[0]);
		$col = DMCollectionFactory::getCollection($alias);
		$ptr = (int) $tmp[1];

		try {
			$obj = DMObjectFactory::getObject($col, $ptr);
			$rep->setBody($trans->transformObject($obj));
		} catch (DMUnavailableModelException $e) {
			$response->setStatusCode(404);
			$rep->setBody($trans->transformException($e));
		}

		$response->send();
		die;
	}

}
